package oop;

import map.Map;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import menu.Menu;
import java.util.Scanner;

/**
 * Lớp Game quản lý loop game, hiển thị
 *
 * @author TxGVNN
 */
public class Game implements Runnable {

    public boolean running;         // game đang chạy?
    public boolean runGame = false; // Game chạy hay Menu chạy?

    Main main;
    Menu menu;

    Map map;            // Bản đồ quái
    Player player;      // Người chơi 
    Bot bot;            // Quái vật

    Graphics graphics;          // Lớp Graphics, cần để vẽ lên màn hình
    BufferStrategy buffer;
    private int idMap = 1;  // Map thứ mấy?
    private byte flagShow = 0x00; //showMenu

    /* use flagShow:
     *@ 0x00: show menu begin
     *@ 0x01: show menu choose map 
     *@ 0x02: runGame only a map 
     *@ 0x03: show menu contiue play
     *@ 0x04: show menu back to menu
     *@ 0x05: show menu about
     *@ 0x06: show menu enter Player's name
     *@ */
    //================================//
    /**
     * Truyền main vào, sử dụng để vẽ
     */
    public Game(Main main) {
        this.main = main;
        menu = new Menu(this);
        buffer = main.getBufferStrategy();
        main.addMouseListener(menu);
    }

    /**
     * Khởi chạy game
     */
    public synchronized void start() {
        running = true;
        new Thread(this).start(); // Gọi luồng Game và start => run() chạy

    }

    /**
     * Thoát game, kết thúc loop(hủy thread) và đóng của sổ
     */
    public synchronized void stop() {
        running = false;
        main.exitGame();

    }

    /**
     * Khởi tạo tất cả các đối tượng game từ data.txt
     */
    public void init(int id) {
        Scanner sc = new Scanner(getClass().getResourceAsStream("/data.txt"));
        while (id-- > 0) {
            sc.nextLine();
        }
        // Lấy id map
        map = new Map(sc.next(), sc.nextInt());

        // Khởi tao nhân vật và cài thuộc tính
        player = new Player(sc.nextInt(), sc.nextInt(), sc.next());
        player.setProperty(sc.nextInt(), sc.nextDouble(), sc.nextDouble(), sc.nextDouble(), sc.nextDouble());
        // Khởi tao Bot và cài thuộc tính
        bot = new Bot(sc.nextInt(), sc.nextInt(), sc.next());
        bot.setProperty(sc.nextInt(), sc.nextDouble(), sc.nextDouble(), sc.nextDouble(), sc.nextDouble());

        bot.setRival(player);   // cài đặt đối thủ
        player.setRival(bot);   // cài đặt đối thủ

        main.addKeyListener(player);
    }

    /**
     * Cài cở hoạt động, hiển thị. Chi tiết phần khai báo flagShow
     *
     * @param flagShow
     */
    public void setFlag(int flagShow) {
        this.flagShow = (byte) flagShow;
        switch (flagShow) {
            case 0x00:
            case 0x01:
            case 0x03:
            case 0x04:
            case 0x05:
                /// Chạy tương tác menu
                if (runGame) {
                    runGame = false;
//                    main.addMouseListener(menu);
                }
                break;
            case 0x02:
                // Chạy game
                if (!runGame) {
                    runGame = true;
//                    main.removeMouseListener(menu);
                }

                break;
        }
    }

    public int getFlag() {
        return flagShow;
    }

    /**
     * Hàm update tất cả các thuộc tính của map, nhân vật, bot, hiệu ứng
     */
    private void updateGameState() {
        // Player đã chết
        if (!player.isActive()) {
            setFlag(0x04);// hiển thị người chơi thua
            return;
        }
        // Bot đã chết
        if (!bot.isActive()) {
            setFlag(0x03); //// hiển thị người chơi thắng
            return;
        }

        bot.auto();

    }

    /**
     * Kiểm tra nhập liệu
     */
    private void checkUserInput() {
        player.control(bot);
    }

    /**
     * Update lại màn hình
     */
    private void updateGameScreen(Graphics g) {
        try {
            g = buffer.getDrawGraphics();

            // ******** BEGIN-MENU *************
            // Vẽ menu 
            if (!runGame) {

                if (flagShow == 0x03 || flagShow == 0x04) {
                    // người chơi thắng thua
                    // vẫn vẽ map, player, bot nhìn cho đẹp
                    map.paint(g);
                    player.paint(g);
                    bot.paint(g);
                }
                menu.paint(g);
            } // ******** END-MENU *************
            // ******** BEGIN-GAME **********
            // Vẽ game
            else {

                // Vẽ lại map...
                map.paint(g);

                // Vẽ lại vị trí, thuộc tính của Player
                player.paint(g);

                // Vẽ lại các bot
                bot.paint(g);
            }
            // ******** END-GAME **********

            if (!buffer.contentsLost()) {
                buffer.show();
            }

        } finally {

            if (g != null) {
                g.dispose();
            }
        }
    }

    /**
     * Cài dặt hàm run của Runnable
     */
    @Override
    public void run() {
        /**
         * Tính toán thời gian delay cho chân thật. Vài biến còn lại để test
         * chưa Quan trọng 3 hàm update, input, render. Hiện tại là 12 lần/ 1
         * giây
         */
        long msPerFr = 1000 / 20;   // milisecs / FPS số mili giây trên một frame, có nghĩa là 1 vòng lặp cần bao nhiêu milisecs
        long URDTime;             // Thời gian dành cho Update, render, draw
        long waitTime;            // Thời gian dư thừ cần sleep

        int frameCount = 0;       // Test: đếm số frame trong 1 giây
        int maxFrameCount = 20; // Test: max số frame trong 1 giây

        long startTime;         // Thời gian bắt đầu loop game
        long totalTime = 0;     // Tổng thời gian loop trong 1 giây
        while (running) {
            startTime = System.nanoTime();

            //=========================//
            if (runGame) {
                updateGameState();
                checkUserInput();
            }
            updateGameScreen(graphics);
            //=========================//

            URDTime = (System.nanoTime() - startTime) / 1000000;
            waitTime = msPerFr - URDTime;  // Chưa đủ thời gian loop thì sleep thread lại
            if (waitTime < 0) {
                waitTime = 5;
            }
            try {
                Thread.sleep(waitTime);
            } catch (Exception e) {
                e.printStackTrace();
            }

            /* Begin-Test: Kiểm tra số  frame trong 1 giây */
            totalTime += System.nanoTime() - startTime;
            frameCount++;
            if (frameCount == maxFrameCount) {
                main.jFrame.setTitle("Game: " + 1000.0 / ((totalTime / frameCount) / 1000000) + " FPS");
                frameCount = 0;
                totalTime = 0;
            }
            /* End-Test */
        }

    }
}
